/*
 * Copyright (C) 2010-2011 Comcast Cable.  All Rights Reserved.
 */
package com.hercules.android.receiver.connection.parser.json.itemparsers;

import com.google.gjson.JSONArray;
import com.google.gjson.JSONException;
import com.google.gjson.JSONObject;
import com.hercules.android.receiver.connection.parser.CommandParsingException;
import com.hercules.android.receiver.connection.parser.MalformedCommandException;

/**
 * Parser wrapper for runtime properties.
 * <p>
 * AndroidAssembly2 this class was added in Android Assembly 2
 *
 * @author 7realm
 * @version 1.0
 */
public class RuntimePropertyParser extends FieldParser {
    /** Represents inner parser. */
    private final FieldParser innerParser;

    /**
     * Constructor for {@link RuntimePropertyParser}.
     *
     * @param innerParser the inner parser
     */
    public RuntimePropertyParser(FieldParser innerParser) {
        super(innerParser.isRequired());
        this.innerParser = innerParser;
    }

    /**
     * Parses runtime property. First try to parse property as normal. If this fails, parse it as runtime property.
     *
     * @param path the path to value
     * @param json the JSON
     * @param keyName the name of current JSON key
     * @param instance the instance of object, parsed value will be set to it
     * @return the parsed value
     * @throws CommandParsingException if error occurs while parsing
     */
    @Override
    protected Object doParse(StringBuilder path, JSONObject json, String keyName, Object instance)
        throws CommandParsingException {
        try {
            // try to parse with inner parser
            return innerParser.doParse(path, json, keyName, instance);
        } catch (CommandParsingException e) {
            // if failed to parse, then make this property runtime
            if (innerParser instanceof ArrayParser) {
                ArrayParser arrayParser = (ArrayParser) innerParser;
                return parseList(path, json, keyName, arrayParser.getArrayLength());
            } else if (innerParser instanceof MatrixParser) {
                return parseList(path, json, keyName, 16);
            } else {
                // integer and double parsers
                return STRING_REQUIRED.doParse(path, json, keyName, instance);
            }
        }
    }

    /**
     * Parses JSON array to string list.
     *
     * @param path the path to value
     * @param json the JSON
     * @param keyName the name of current JSON key
     * @param count the expected count of items in array
     * @return the parsed value
     * @throws MalformedCommandException if error occurs while parsing
     */
    private static String[] parseList(StringBuilder path, JSONObject json, String keyName, int count)
        throws MalformedCommandException {
        try {
            JSONArray array = json.get(keyName).isArray();

            // check elements count
            if (array.size() != count) {
                throw new MalformedCommandException("Incorrect count of array elements in '" + path
                    + "', expected '" + count + "'. JSON '" + json + "'.");
            }

            // add items to result
            String[] result = new String[count];
            for (int i = 0; i < count; i++) {
                String string = array.get(i).isString().stringValue().trim();
                if (!string.startsWith("{")) {
                    string = "{" + string + "}";
                }
                result[i] = string;
            }

            return result;
        } catch (JSONException e) {
            throw new MalformedCommandException("Incorrect JSON array at '" + path + "', at JSON '"
                + json + "'.", e);
        }
    }

}
